#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>


#include "global.h"
#include "constantes.h"
#include "listas.h"
#include "sockets.h"
#include "configuracion.h"
#include "NIPC.h"
#include "RAID.h"

extern ListConfig * listaConfig;
extern pthread_mutex_t mutex;  /*Para manejar cola global de pedidos de lectura y escritura*/
extern chs glChs;

int main ()
{	
   
    InfoIPC rInfoIPC;
    struct sockaddr_in Str_sockaddr_in_paraAcept,sin;
    int iAddress_size= sizeof(struct sockaddr_in);
    fd_set set_socketsHiloPrincipal;			/**lista de sockets que se van a hacer que se active el select del hilo principal cuando reciban algo**/		
    int iSocketEscuchaPedidosSincro,iSocketEscuchaPedidosFSS,iSocket_temporal,iMayorSocket;	
	int *pSocketAux,*pSocketArrayParaConsola;	
    struct DiscoConocido * pAuxDisco,* disk,*pDiscoSinc;
    pthread_t ID_HiloParaConsolaEsperarConexionDiscos;
    int ret,i;
	int contador=0;	
	cliente *pNodoCliente,*pAuxCliente;
	char * sContador=(char*)malloc(20);
	char *sSocket=(char *)malloc(10);
	
    /*****INICIALIZACION DE VARIABLES******/
    Estado= FUERA_DE_SERVICIO;
    SubEstado = INCOMPLETO;
    ListaDiscos = NULL;
    ListaDiscos_Ult = NULL;
	ListaClientes = NULL;
    ListaClientes_Ult = NULL;	
	colaGlobal=NULL;	
	colaGlobal_Ult=NULL;
    /*ArrayCompleto = FALSO;*/
    /************************************/
	listaConfig = NULL;
	cargarConfig();
	
	system("clear");
	
	printf("\nCargando Archivo de Configuracion...\n\n");
    printf("puertoFSS %s \n",obtenerValor("puertoFSS"));
    printf("puertoConex %s \n",obtenerValor("puertoConex"));
    printf("puertoSinc %s \n",obtenerValor("puertoSinc"));
    printf("minimoDiscos %s \n",obtenerValor("minimoDiscos"));
    printf("CHS %s \n",obtenerValor("CHS"));
    printf("nombreDiscos %s \n",obtenerValor("nombreDiscos"));
   
	
	iCantidadDiscosDeArchivoConfig=atoi(obtenerValor("minimoDiscos"));
   
	pDiscosDeArchivoConfig = malloc( sizeof(struct DiscoDeArchivoConfig) * iCantidadDiscosDeArchivoConfig );
      
	for(i=0 ; i< iCantidadDiscosDeArchivoConfig ; i++)
	{	
      strcpy(pDiscosDeArchivoConfig[i].sNombre,obtenerValor("nombreDiscos"));
      pDiscosDeArchivoConfig[i].sNombre[ strlen ( pDiscosDeArchivoConfig[i].sNombre ) - 1 ] = '\0';
      pDiscosDeArchivoConfig[i].Conectado = FALSO;
	}
   
	configToCHS();	
	pSocketArrayParaConsola =malloc(sizeof(int));
	pSocketAux = malloc(sizeof(int));						
	CrearYConectarDosSockets(pSocketArrayParaConsola,pSocketAux,obtenerValor("ipLocal"));
	ret=pthread_create(&ID_HiloParaConsolaEsperarConexionDiscos,NULL,(void *)ConsolaEsperaNuevosDiscos ,(void *)pSocketAux);    
	recibirIPC(*pSocketArrayParaConsola, &rInfoIPC); /*RAID pasa a estar operacional*/
	

	iSocketEscuchaPedidosFSS=PrepararSocket (atoi(obtenerValor("puertoFSS")),obtenerValor("ipLocal"));   
	escuchar(iSocketEscuchaPedidosFSS,10);
    
	while(1)
	{			
		FD_ZERO(&set_socketsHiloPrincipal);
		FD_SET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal);
		iMayorSocket = iSocketEscuchaPedidosFSS;

	
		pAuxCliente = ListaClientes;
		while(pAuxCliente != NULL)
		{
			FD_SET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal);	
			printf("\nCliente Agregado: socket %i\n", pAuxCliente->socketCliente);
			if(pAuxCliente->socketCliente > iMayorSocket)
				iMayorSocket = pAuxCliente->socketCliente;
			pAuxCliente = pAuxCliente->Sgt;
		}
	
		
		select(iMayorSocket + 1,&set_socketsHiloPrincipal, NULL, NULL, NULL);
       
		
		/*Acepto sockets para pedidos del FSS*/
		if( FD_ISSET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal ) > 0 )
		{
			if((iSocket_temporal = accept (iSocketEscuchaPedidosFSS,(struct sockaddr *)&sin,&iAddress_size ))== -1)
				return ERRORACCEPT;	
			printf("\nSe conecto un cliente FSS.El descriptor temporal devuelto es %i\n",iSocket_temporal);
			
			recibirIPC(iSocket_temporal,&rInfoIPC);
		
			if (iCantidadDeDAConectados == 0) {
                printf("\n\nConexion rechazada, no hay discos conectados \n\n");
				rInfoIPC.payLoadLength=5;
				generarStructIPC("nada",TERMINARCONEXION,&rInfoIPC);
				enviarIPC(iSocket_temporal,&rInfoIPC);
				recibirIPC(iSocket_temporal,&rInfoIPC);
				cerrarSocket(iSocket_temporal);
				continue;
			}			
			
			
			pNodoCliente = (cliente *)malloc(sizeof(cliente));
			pNodoCliente->socketCliente=iSocket_temporal;
			
			agregarAListaDeClientes(&pNodoCliente);
			
			rInfoIPC.payLoadLength=5;
			generarStructIPC("nada",HSOK,&rInfoIPC);
			enviarIPC(pNodoCliente->socketCliente,&rInfoIPC);
			
		}	
		
		
		/*RECIBO PEDIDOS DE ALGUN SOCKET CLIENTE FSS*/
		pAuxCliente = ListaClientes;
		while(pAuxCliente != NULL)
		{
			if( FD_ISSET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal ) > 0 ){
				break;
			}
			pAuxCliente = pAuxCliente->Sgt;
		}
		if(pAuxCliente != NULL)
		{	
			
			struct ListaIDdescriptors * NodoDescriptores = (struct ListaIDdescriptors *)malloc(sizeof(struct ListaIDdescriptors));	
			
			/*Se desconecta cliente*/
			if(recibirIPC(pAuxCliente->socketCliente,&rInfoIPC)<0){	
				printf("Se cierra cliente \n");
				close(pAuxCliente->socketCliente);
				FD_CLR(pAuxCliente->socketCliente, &set_socketsHiloPrincipal);
				SacarDeLista_Clientes(&pAuxCliente);
				free(pAuxCliente);
				continue;
			}
			
			printf("No se cerro cliente \n");
			
			intacad(pAuxCliente->socketCliente,sSocket);  
			strcpy(NodoDescriptores->sSocketPorElCualResponder , sSocket);	
			NodoDescriptores->numeroSector=atoi(rInfoIPC.payload);
			NodoDescriptores->Sgt = NULL;
			NodoDescriptores->Ant = NULL;
			
			
			if((rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST) || (rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST_LAST))
			{
				NodoDescriptores->operacion=0;
				printf("Recibi pedido de lectura de %i\n",atoi(rInfoIPC.payload));
			}
			
			if(rInfoIPC.payLoadDescriptor == WRITE_SECTOR_REQUEST)
			{
				/*Recibo la data*/
				printf("Recibi pedido de escritura de %i\n",atoi(rInfoIPC.payload));
				
						rInfoIPC.payLoadLength = 5;
						generarStructIPC("nada",RTA_WRITE_SECTOR_REQUEST,&rInfoIPC);
						enviarIPC(pAuxCliente->socketCliente,&rInfoIPC);
						
				recibirIPC(pAuxCliente->socketCliente,&rInfoIPC);	
				// if(!(rInfoIPC.payLoadDescriptor == SET_SECTORES  || rInfoIPC.payLoadDescriptor == SET_SECTORES_LAST))
				// {
					// printf("  recibirIPC2() failed\n");
					// continue;
				// }
				          
				NodoDescriptores->operacion=1;
				memset(NodoDescriptores->data,'\0',512);   
				memcpy(NodoDescriptores->data,rInfoIPC.payload, 512);
				
			}
			
			contador++;			
			intacad(contador,sContador);
			strcpy(NodoDescriptores->sDescriptorID_delPedido,sContador);
			
			asignarPedido(&NodoDescriptores);
			
			/*Lo agrego a la cola global(CREO QUE SE PODRA SUPRIMIR)*/
			if(pthread_mutex_lock(&mutex)!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}
			
			AgregarAColaGlobal(&NodoDescriptores,&colaGlobal,&colaGlobal_Ult);
			imprimirCola2(colaGlobal);
		
			if(pthread_mutex_unlock(&mutex)!=0){
				perror("pthread_mutex_unlock");
				exit(EXIT_FAILURE);
			}
			
		}
		
	}
	free(pSocketArrayParaConsola);
	free(pSocketAux);
	return 0;
}


void configToCHS()
{
	char* chsAux;
	chs resultadoChs;

	chsAux = (char*)malloc(strlen(obtenerValor("CHS"))+1);
	strcpy(chsAux,obtenerValor("CHS"));
	resultadoChs.cilindros = atoi(strtok(chsAux,","));
	resultadoChs.cabezal = atoi(strtok(NULL,","));
	resultadoChs.sectores = atoi(strtok(NULL,","));
	glChs = resultadoChs;

	free(chsAux);
}

chs getCHS()
{
	return glChs;
}

void asignarPedido(struct ListaIDdescriptors ** nodoPedido) {
   
	char* sNumeroSector=(char *)malloc(20);
	disco * pAuxDisco;  
	InfoIPC rInfoIPC;
	
	if ((*nodoPedido)->operacion==0) {
		
		disco *dsk = (struct DiscoConocido *)elegirDisco((*nodoPedido)->numeroSector);
		printf("Disco elegido %s \n",dsk->sNombre);	
		dsk->pendings++;
		
		/*Lo agrego a la lista de pedidos del disco*/
		if(pthread_mutex_lock(&(dsk->mutex))!=0){
			perror("pthread_mutex_lock");
			exit(EXIT_FAILURE);
		}
	
		AgregarAColaDePedidosADisco(&(*nodoPedido),&(dsk->listaPedidos),&(dsk->listaPedidos_Ult));
		imprimirCola2(dsk->listaPedidos);
	
		if(pthread_mutex_unlock(&(dsk->mutex))!=0){
			perror("pthread_mutex_unlock");
			exit(EXIT_FAILURE);
		}
			
		
		/*Le mando al hilo el numero del pedido*/
		rInfoIPC.payLoadLength=strlen((*nodoPedido)->sDescriptorID_delPedido)+1;
		generarStructIPC((*nodoPedido)->sDescriptorID_delPedido,NUMEROPEDIDO,&rInfoIPC);
		enviarIPC(*(dsk->iSocketHDAParaArray),&rInfoIPC);
		
		/*Le mando al hilo el numero del sector*/
		strcpy((*nodoPedido)->discoAsignado,dsk->sNombre);
		intacad((*nodoPedido)->numeroSector,sNumeroSector);
		rInfoIPC.payLoadLength=strlen(sNumeroSector)+1;
		generarStructIPC(sNumeroSector,READ_SECTOR_REQUEST,&rInfoIPC);
		enviarIPC(*(dsk->iSocketHDAParaArray),&rInfoIPC);
			
		CambiaPosicionDelNodo(&dsk);
		
		
	} else {
		
		strcpy((*nodoPedido)->discoAsignado,"");
		(*nodoPedido)->discosAEspejar=longitud2(ListaDiscos);
		pAuxDisco = ListaDiscos;
		
		while(pAuxDisco != NULL){	
							
			pAuxDisco->pendings++;
			
			/*Lo agrego a la lsita de pedidos del disco*/
			if(pthread_mutex_lock(&(pAuxDisco->mutex))!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}

			AgregarAColaDePedidosADisco(&(*nodoPedido),&(pAuxDisco->listaPedidos),&(pAuxDisco->listaPedidos_Ult));
			imprimirCola2(pAuxDisco->listaPedidos);
	
			if(pthread_mutex_unlock(&(pAuxDisco->mutex))!=0){
				perror("pthread_mutex_unlock");
				exit(EXIT_FAILURE);
			}
			
			
			/*Le mando al hilo el numero del pedido*/
			rInfoIPC.payLoadLength=strlen((*nodoPedido)->sDescriptorID_delPedido)+1;
			generarStructIPC((*nodoPedido)->sDescriptorID_delPedido,NUMEROPEDIDO,&rInfoIPC);
			enviarIPC(*(pAuxDisco->iSocketHDAParaArray),&rInfoIPC);
			
			/*Le mando al hilo el numero del sector*/
			intacad((*nodoPedido)->numeroSector,sNumeroSector);
			rInfoIPC.payLoadLength=strlen(sNumeroSector)+1;
			generarStructIPC(sNumeroSector,WRITE_SECTOR_REQUEST,&rInfoIPC);
			enviarIPC(*(pAuxDisco->iSocketHDAParaArray),&rInfoIPC);
		
			/*Le mando al hilo del disco la data*/
			rInfoIPC.payLoadLength=512;
			generarStructIPC((*nodoPedido)->data,WRITE_SECTOR_REQUEST,&rInfoIPC);
			enviarIPC(*(pAuxDisco->iSocketHDAParaArray),&rInfoIPC);
			
			pAuxDisco=pAuxDisco->Sgt;
		
		}	
	}
	
}

disco * elegirDisco(int numeroSector) {
	
	int count = 32767;
	disco *pAuxDisco;
	disco *pDiscoElegido;
	
	pAuxDisco=NULL;
	pDiscoElegido=NULL;
	
	pAuxDisco = ListaDiscos;
	
	while(pAuxDisco != NULL){
	
		printf("Disco: %s \n",pAuxDisco);
		printf("SECTORES: %i \n", pAuxDisco->sectores);
		printf("PENDINGS: %i \n", pAuxDisco->pendings);
		printf("-------------\n");
		
		if ((numeroSector < pAuxDisco->sectores) && (pAuxDisco->pendings < count)) {
			pDiscoElegido=pAuxDisco;
			count = pAuxDisco->pendings;
		} 
		
		pAuxDisco=pAuxDisco->Sgt;
		
	}
	
    return pDiscoElegido;
}	
